#include "driver/gpio.h"
#include "api_map.h"
#include "dvc.h"
#include "dvc_log.h"

#define AES_LED 1
extern dvc_t gloab_dvc;

static void send_logs(httpd_req_t *req, uint16_t log_count, uint16_t buf_max, dvc_log_t logs[])
{
    http_server_t server = httpd_get_global_user_ctx(req->handle);
    char *str = (char *)server->data;
    uint16_t len = 0;
    bool last_send = false;
    if (log_count > buf_max)
        log_count = log_count % buf_max;
    /* old */
    for (uint16_t i = log_count; i < buf_max; i++)
    {
        dvc_log_t *log = &logs[i % buf_max];
        if (!log->timestamp)
            continue;
        if (last_send)
            httpd_resp_sendstr_chunk(req, ",");
        len = dvc_log_print_to(gloab_dvc, log, str, sizeof(server->data));
        if (len)
        {
            last_send = true;
            httpd_resp_send_chunk(req, str, len);
        }
    }
    if (log_count)
    {
        /* new */
        for (uint16_t i = 0; i < log_count; i++)
        {
            dvc_log_t *log = &logs[i % buf_max];
            if (!log->timestamp)
                continue;
            if (last_send)
                httpd_resp_sendstr_chunk(req, ",");
            len = dvc_log_print_to(gloab_dvc, log, str, sizeof(server->data));
            if (len)
            {
                last_send = true;
                httpd_resp_send_chunk(req, str, len);
            }
        }
    }
}

static void send_event_logs(httpd_req_t *req, uint16_t log_count, uint16_t buf_max)
{
    http_server_t server = httpd_get_global_user_ctx(req->handle);
    char *str = (char *)server->data;
    uint16_t len = 0;
    bool last_send = false;
    if (log_count > buf_max)
        log_count = log_count % buf_max;
    /* old */
    for (uint16_t i = log_count; i < buf_max; i++)
    {
        dvc_log_event_t *log = &gloab_dvc->log.event[i % buf_max];
        if (!log->timestamp)
            continue;
        if (last_send)
            httpd_resp_sendstr_chunk(req, ",");
        len = dvc_log_event_print_to(gloab_dvc, log, str, sizeof(server->data));
        if (len)
        {
            last_send = true;
            httpd_resp_send_chunk(req, str, len);
        }
    }
    if (log_count)
    {
        /* new */
        for (uint16_t i = 0; i < log_count; i++)
        {
            dvc_log_event_t *log = &gloab_dvc->log.event[i % buf_max];
            if (!log->timestamp)
                continue;
            if (last_send)
                httpd_resp_sendstr_chunk(req, ",");
            len = dvc_log_event_print_to(gloab_dvc, log, str, sizeof(server->data));
            if (len)
            {
                last_send = true;
                httpd_resp_send_chunk(req, str, len);
            }
        }
    }
}
esp_err_t get_logs(httpd_req_t *req)
{
    httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
    httpd_resp_set_hdr(req, "Access-Control-Allow-Methods", "POST,OPTIONS");
    httpd_resp_sendstr_chunk(req, "{\"api\":\"get_logs\",\"data\":{");
#if defined(DVC_ENABLE_LOG)
    dvc_t dvc = gloab_dvc;
    httpd_resp_sendstr_chunk(req, "\"infos\":{\"sec\":[");
    send_logs(req, dvc->log.sec_w, 60, gloab_dvc->log.sec);
    httpd_resp_sendstr_chunk(req, "],\"min\":[");
    send_logs(req, dvc->log.min_w, 60, gloab_dvc->log.min);
    httpd_resp_sendstr_chunk(req, "],\"hour\":[");
    send_logs(req, dvc->log.hour_w, 24, gloab_dvc->log.hour);
    httpd_resp_sendstr_chunk(req, "],\"day\":[");
    send_logs(req, dvc->log.day_w, 1440, gloab_dvc->log.day);
    httpd_resp_sendstr_chunk(req, "],\"years\":[");
    send_logs(req, gloab_dvc->log.years_w, 366, gloab_dvc->log.years);
#if defined(DVC_ENABLE_EVENT_LOG)
    httpd_resp_sendstr_chunk(req, "]},");
#else
    httpd_resp_sendstr_chunk(req, "]}");
#endif
#endif

#if defined(DVC_ENABLE_EVENT_LOG)
    httpd_resp_sendstr_chunk(req, "\"events\":[");
    send_event_logs(req, gloab_dvc->log.event_w, 1440);
    httpd_resp_sendstr_chunk(req, "]");
#endif

    httpd_resp_sendstr_chunk(req, "}}");
    httpd_resp_sendstr_chunk(req, NULL);
    return ESP_OK;
}

static void afe_clear_alert_process(http_server_t server, httpd_ws_frame_t *frame, void *data)
{
    dvc_clear_alert(gloab_dvc, DVC_EVENT_FLAG_USER);
    return ws_reply_status(server, frame, "afe_clear_alert", 0);
}

static void afe_set_chg(http_server_t server, httpd_ws_frame_t *frame, void *data)
{
    if (!cJSON_HasObjectItem(data, "enable"))
        return ws_reply_status(server, frame, "afe_set_chg", -1);
    cJSON *enable_obj = cJSON_GetObjectItem(data, "enable");
    if (!cJSON_IsBool(enable_obj))
        return ws_reply_status(server, frame, "afe_set_chg", -1);
    uint8_t enable = cJSON_IsTrue(enable_obj);
    dvc_chg(gloab_dvc, enable, DVC_EVENT_FLAG_USER); /* enable charge */
    dvc_config_commit(gloab_dvc);
    return ws_reply_status(server, frame, "afe_set_chg", 0);
}

static void afe_set_dsg(http_server_t server, httpd_ws_frame_t *frame, void *data)
{
    if (!cJSON_HasObjectItem(data, "enable"))
        return ws_reply_status(server, frame, "afe_set_dsg", -1);
    cJSON *enable_obj = cJSON_GetObjectItem(data, "enable");
    if (!cJSON_IsBool(enable_obj))
        return ws_reply_status(server, frame, "afe_set_dsg", -1);
    uint8_t enable = cJSON_IsTrue(enable_obj);
    dvc_dsg(gloab_dvc, enable, DVC_EVENT_FLAG_USER); /* enable discharge */
    dvc_config_commit(gloab_dvc);
    return ws_reply_status(server, frame, "afe_set_dsg", 0);
}

static void afe_set_ae(http_server_t server, httpd_ws_frame_t *frame, void *data)
{
    if (!cJSON_HasObjectItem(data, "enable"))
        return ws_reply_status(server, frame, "afe_set_ae", -1);
    cJSON *enable_obj = cJSON_GetObjectItem(data, "enable");
    if (!cJSON_IsBool(enable_obj))
        return ws_reply_status(server, frame, "afe_set_ae", -1);
    uint8_t enable = cJSON_IsTrue(enable_obj);
    dvc_aes(gloab_dvc, enable, DVC_EVENT_FLAG_USER); /* enable ae */
    ws_reply_status(server, frame, "afe_set_ae", 0);
}

static void afe_get_config(http_server_t server, httpd_ws_frame_t *frame, void *data)
{
    char *str = (char *)server->data;
    uint16_t len = 0;
    len += sprintf(&str[len], "{");
    len += sprintf(&str[len], "\"api\":\"%s\",", "afe_get_config");
    len += sprintf(&str[len], "\"data\":");
    len += dvc_user_config_print_to(&gloab_dvc->user_cfg, &str[len], sizeof(server->data) - len);
    len += sprintf(&str[len], "}");
    frame->payload = server->data;
    frame->len = len;
}

static void clear_event_log(http_server_t server, httpd_ws_frame_t *frame, void *data)
{
    extern void event_clear(void);
    event_clear();
    dvc_log_event_clear(gloab_dvc);
    ws_reply_status(server, frame, "clear_event_log", 1);
}

static void clear_log(http_server_t server, httpd_ws_frame_t *frame, void *data)
{
    extern void log_clear(void);
    log_clear();
    dvc_log_clear(gloab_dvc);
    ws_reply_status(server, frame, "clear_log", 1);
}
static void afe_update_config(http_server_t server, httpd_ws_frame_t *frame, void *data)
{
    if (!data || !cJSON_IsObject(data))
        goto afe_update_config_error;
    /**
        {
            "uR": 1000,
            "mAH": 20000,
            "aes": {
                "start": 0.05,
                "end": 0.005"
            },
            "afe":{
                "ovt":1,
                "uvt":1,
                "occt":1,
                "ocdt":1,
                "ocdd":1,
                "occd":1,
                "scdd":1,
                "scdt":1,
                "cp_gc":1,
                "cs_pt":1,
                "vs_pt":1,
                "bat_num":1,
                "covt":1,
                "cuvt":1,
                "cccd":1,
                "cscd":1,
                "calib_v":0.99,
                "calib_c":0.93,
            }
        }
    */
    static const char *const obj_keys[] = {
        "uR",
        "mAH",
        "aes",
        "afe",
    };
#define obj_keys_len sizeof(obj_keys) / sizeof(obj_keys[0])
    cJSON *obj_list[obj_keys_len] = {0};
    for (uint8_t i = 0; i < obj_keys_len; i++)
    {
        if (!cJSON_HasObjectItem(data, obj_keys[i]))
            goto afe_update_config_error;
        obj_list[i] = cJSON_GetObjectItem(data, obj_keys[i]);
    }

    static const char *const aes_keys[] = {
        "start",
        "end",
    };
#define aes_keys_len sizeof(aes_keys) / sizeof(aes_keys[0])
    cJSON *aes_list[aes_keys_len] = {0};
    for (uint8_t i = 0; i < aes_keys_len; i++)
    {
        if (!cJSON_HasObjectItem(obj_list[2], aes_keys[i]))
            goto afe_update_config_error;
        aes_list[i] = cJSON_GetObjectItem(obj_list[2], aes_keys[i]);
    }

    static const char *const afe_keys[] = {
        "ovt",
        "uvt",
        "occt",
        "ocdt",
        "ocdd",
        "occd",
        "scdd",
        "scdt",
        "cp_gc",
        "cs_pt",
        "vs_pt",
        "bat_num",
        "covt",
        "cuvt",
        "cccd",
        "cscd",
        "calib_v",
        "calib_c",
        "calib_f",
        "calib_p",
    };
#define afe_keys_len sizeof(afe_keys) / sizeof(afe_keys[0])
    cJSON *afe_list[afe_keys_len] = {0};
    for (uint8_t i = 0; i < afe_keys_len; i++)
    {
        if (!cJSON_HasObjectItem(obj_list[3], afe_keys[i]))
            goto afe_update_config_error;
        afe_list[i] = cJSON_GetObjectItem(obj_list[3], afe_keys[i]);
    }

    dvc_user_cfg_t cfg = {0};
    cfg.current_R = obj_list[0]->valueint;
    cfg.bat_mAH = obj_list[1]->valueint;
    cfg.aes.max = cJSON_GetNumberValue(aes_list[0]);
    cfg.aes.min = cJSON_GetNumberValue(aes_list[1]);
    cfg.afe.ovt = afe_list[0]->valueint;
    cfg.afe.uvt = afe_list[1]->valueint;
    cfg.afe.occt = afe_list[2]->valueint;
    cfg.afe.ocdt = afe_list[3]->valueint;
    cfg.afe.ocdd = afe_list[4]->valueint;
    cfg.afe.occd = afe_list[5]->valueint;
    cfg.afe.scdd = afe_list[6]->valueint;
    cfg.afe.scdt = afe_list[7]->valueint;
    cfg.afe.cp_gc = afe_list[8]->valueint;
    cfg.afe.cs_pt = afe_list[9]->valueint;
    cfg.afe.vs_pt = afe_list[10]->valueint;
    cfg.afe.bat_num = afe_list[11]->valueint;
    cfg.afe.covt = afe_list[12]->valueint;
    cfg.afe.cuvt = afe_list[13]->valueint;
    cfg.afe.cccd = afe_list[14]->valueint;
    cfg.afe.cscd = afe_list[15]->valueint;
    cfg.calib.voltage = (float)afe_list[16]->valuedouble;
    cfg.calib.current = (float)afe_list[17]->valuedouble;
    cfg.calib.filter_current = (float)afe_list[18]->valuedouble;
    cfg.calib.consumable = (float)afe_list[19]->valuedouble;
    if (!memcmp(&cfg, &gloab_dvc->cfg, sizeof(dvc_user_cfg_t)))
        return ws_reply_data(server, frame, "afe_update_config", "No change");
    uint8_t failed = dvc_config_user_cfg_update(gloab_dvc, &cfg);
    return ws_reply_status(server, frame, "afe_update_config", failed);
afe_update_config_error:
    return ws_reply_data(server, frame, "afe_update_config", "Incorrect/incomplete parameters");
}
void dvc_api_init(void)
{
    ADD_API_ITEM_ALIAS("afe_clear_alert", afe_clear_alert_process);
    ADD_API_ITEM(afe_set_chg);
    ADD_API_ITEM(afe_set_dsg);
    ADD_API_ITEM(afe_set_ae);
    ADD_API_ITEM(afe_get_config);
    ADD_API_ITEM(afe_update_config);
    ADD_API_ITEM(clear_event_log);
    ADD_API_ITEM(clear_log);
}
